#include "Vision.h"

#include <QDebug>
#include <random>


HImage Vision_API::HImageFromMat(cv::Mat& src)
{
    HImage hImage;
    hImage.Clear();
    uchar* data = (uchar*)src.data;
    int width = src.cols;
    int height = src.rows;
    int bitDepth = src.channels() * 8;
    switch (bitDepth)
    {
    case 8:
        HalconCpp::GenImage1Extern(&hImage, "byte", width, height, (Hlong)data, NULL);
        break;
    case 16:
        HalconCpp::GenImage1Extern(&hImage, "int2", width, height, (Hlong)data, NULL);
        break;
    case 24:
        hImage.GenImageInterleaved(data, "bgr", width, height, -1, "byte", 0, 0, 0, 0, -1, 0);
        break;
    case 32:
        hImage.GenImageInterleaved(data, "bgrx", width, height, -1, "byte", 0, 0, 0, 0, -1, 0);
        break;
    default:
        std::cout << "不存在的位深度" << std::endl;
        break;
    }

    return hImage;
}


/**
 * @brief QImage2HImage 将 Qt QImage 转换为 Halcon 的 HImage
 * @param from 输入的 QImage
 * @param to 输出的 HImage ，from 和 to 不共享内存数据。 每次都会为 to 重新分配内存。
 * @return true 表示转换成功，false 表示转换失败。
 */
bool Vision_API::QImage2HImage(QImage& from, HalconCpp::HImage& to)
{
	if (from.isNull()) return false;

	int width = from.width(), height = from.height();
	QImage::Format format = from.format();

	if (format == QImage::Format_RGB32 ||
		format == QImage::Format_ARGB32 ||
		format == QImage::Format_ARGB32_Premultiplied)
	{
		if (from.bytesPerLine() == 4 * width)
		{
			to.GenImageInterleaved(from.bits(), "bgrx", width, height, 0, "byte", width, height, 0, 0, 8, 0);
		}
		else
		{
			to.GenImageInterleaved(from.bits(), "bgrx", width, height, 0, "byte", width, height, 0, 0, 8, 0);
			uchar* R, * G, * B;
			HString Type;
			Hlong Width, Height;
			to.GetImagePointer3(reinterpret_cast<void**>(&R),
				reinterpret_cast<void**>(&G),
				reinterpret_cast<void**>(&B), &Type, &Width, &Height);
			for (int row = 0; row < height; row++)
			{
				QRgb* line = reinterpret_cast<QRgb*>(from.scanLine(row));
				for (int col = 0; col < width; col++)
				{
					*R = qRed(line[col]);
					*G = qGreen(line[col]);
					*B = qBlue(line[col]);
					++R;
					++G;
					++B;
				}
			}
		}
		return true;
	}
	else if (format == QImage::Format_RGB888)
	{
		if (from.bytesPerLine() == 3 * width)
		{
			to.GenImageInterleaved(from.bits(), "rgb", width, height, 0, "byte", width, height, 0, 0, 8, 0);
		}
		else
		{
			to.GenImageInterleaved(from.bits(), "rgb", width, height, 0, "byte", width, height, 0, 0, 8, 0);
			uchar* R, * G, * B;
			HString Type;
			Hlong Width, Height;
			to.GetImagePointer3(reinterpret_cast<void**>(&R),
				reinterpret_cast<void**>(&G),
				reinterpret_cast<void**>(&B), &Type, &Width, &Height);
			for (int row = 0; row < height; row++)
			{
				unsigned char* line = reinterpret_cast<unsigned char*>(from.scanLine(row));
				for (int col = 0; col < width; col++)
				{
					*R++ = *line++;
					*G++ = *line++;
					*B++ = *line++;
				}
			}
		}
		return true;
	}
	else if (format == QImage::Format_Grayscale8 || format == QImage::Format_Indexed8)
	{
		if (from.bytesPerLine() == width)
		{
			to.GenImage1("byte", width, height, from.bits());
		}
		else// 这时说明每行数据之间有填充字节。因此需要重新写数据
		{
			to.GenImageConst("byte", width, height);
			Hlong W, H; HString Type;
			unsigned char* pTo = reinterpret_cast<unsigned char*>(to.GetImagePointer1(&Type, &W, &H));
			for (int row = 1; row < H; row++)
			{
				const unsigned char* pSrc = from.constScanLine(row);
				unsigned char* pDist = pTo + row * W;
				memcpy(pDist, pSrc, static_cast<size_t>(W));
			}
		}
		return true;
	}
	return false;
}

/**
 * @brief HImage2QImage 将 Halcon 的 HImage 转换为 Qt 的 QImage
 * @param from HImage ，暂时只支持 8bits 灰度图像和 8bits 的 3 通道彩色图像
 * @param to QImage ，这里 from 和 to 不共享内存。如果 to 的内存大小合适，那么就不用重新分配内存。所以可以加快速度。
 * @return  true 表示转换成功，false 表示转换失败
 */
bool Vision_API::HImage2QImage(HalconCpp::HImage& from, QImage& to)
{
	Hlong width;
	Hlong height;
	from.GetImageSize(&width, &height);

	HTuple channels = from.CountChannels();
	HTuple type = from.GetImageType();

	if (strcmp(type[0].S(), "byte")) // 如果不是 byte 类型，则失败
	{
		return false;
	}

	QImage::Format format;
	switch (channels[0].I())
	{
	case 1:
		format = QImage::Format_Grayscale8;
		break;
	case 3:
		format = QImage::Format_RGB888;
		break;
	default:
		return false;
	}

	if (to.width() != width || to.height() != height || to.format() != format)
	{
		to = QImage(static_cast<int>(width),
			static_cast<int>(height),
			format);
	}
	HString Type;
	if (channels[0].I() == 1)
	{
		unsigned char* pSrc = reinterpret_cast<unsigned char*>(from.GetImagePointer1(&Type, &width, &height));
		if (to.bytesPerLine() == width)
		{
			memcpy(to.bits(), pSrc, static_cast<size_t>(width) * static_cast<size_t>(height));
		}
		else
		{
			for (int row = 1; row < height; row++)
			{
				unsigned char* pDistLine = to.scanLine(row);
				const unsigned char* pSrcLine = pSrc + row * width;
				memcpy(pDistLine, pSrcLine, static_cast<size_t>(width));
			}
		}

		return true;
	}
	else if (channels[0].I() == 3)
	{
		uchar* R, * G, * B;
		from.GetImagePointer3(reinterpret_cast<void**>(&R),
			reinterpret_cast<void**>(&G),
			reinterpret_cast<void**>(&B), &Type, &width, &height);

		for (int row = 0; row < height; row++)
		{
			unsigned char* line = reinterpret_cast<unsigned char*>(to.scanLine(row));
			for (int col = 0; col < width; col++)
			{
				*line++ = *R++;
				*line++ = *G++;
				*line++ = *B++;
			}
		}
		return true;
	}

	return false;
}

QImage Vision_API::mat2QImage(cv::Mat& mat)
{
	switch (mat.type()) {
	case CV_8UC1: // 灰度图
		return QImage(mat.data, mat.cols, mat.rows,
			mat.step, QImage::Format_Grayscale8);
	case CV_8UC3: // BGR彩色图
		return QImage(mat.data, mat.cols, mat.rows,
			mat.step, QImage::Format_RGB888).rgbSwapped();
	case CV_8UC4: // 带Alpha通道
		return QImage(mat.data, mat.cols, mat.rows,
			mat.step, QImage::Format_ARGB32);
	default:
		return QImage();
	}
}

cv::Mat Vision_API::qImage2Mat(QImage& image)
{
	cv::Mat cvMat;
	qDebug() << image.format();
	switch (image.format())
	{
	case QImage::Format_ARGB32:
	case QImage::Format_RGB32:
		cvMat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
		cv::cvtColor(cvMat, cvMat, cv::COLOR_BGRA2BGR);
		break;
	case QImage::Format_ARGB32_Premultiplied:
		cvMat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
		break;
	case QImage::Format_RGB888:
		cvMat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());
		cv::cvtColor(cvMat, cvMat, cv::COLOR_BGR2RGB);
		break;
	case QImage::Format_Indexed8:
		cvMat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
		break;
	case QImage::Format_Grayscale8:
		cvMat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
		break;
	}
	return cvMat;
}

double Vision_API::rad2Deg(double rad)
{
	HTuple deg;
	TupleDeg(rad, &deg);
	return deg.D();
}

double Vision_API::deg2Rad(double deg)
{
	HTuple rad;
	TupleRad(deg, &rad);
	return rad.D();
}

void Vision_API::drawCross(cv::Mat& src, cv::Point pt, cv::Scalar color, cv::Size sz)
{
	cv::line(src, cv::Point(pt.x - sz.width, pt.y), cv::Point(pt.x + sz.width, pt.y), color);
	cv::line(src, cv::Point(pt.x - sz.width, pt.y), cv::Point(pt.x + sz.width, pt.y), color);
	cv::line(src, cv::Point(pt.x, pt.y - sz.height), cv::Point(pt.x, pt.y + sz.height), color);
}

void Vision_API::textOut(cv::Mat& src, cv::Point pt, cv::Scalar color)
{
	//cv::putText(image,
	//	text,
	//	cv::Point(image.cols - 200, 30), // 确保位置不超出图像范围
	//	cv::FONT_HERSHEY_SIMPLEX,
	//	0.7,
	//	cv::Scalar(0, 0, 255), // 红色
	//	2);
}

void Vision_API::drawHalconXld(cv::Mat& src, HXLDCont& xld, cv::Scalar color)
{
	int objCount = xld.CountObj();
	for (int i = 0; i < objCount; i++)
	{
		auto obj = xld.SelectObj(i + 1);

		cvContour c;
		HTuple rows, cols;
		obj.GetContourXld(&rows, &cols);
		int len = rows.Length();
		for (int j = 0; j < len; j++)
		{
			c.push_back(cv::Point(cols[j].D(), rows[j].D()));
		}

		cv::polylines(src, c, false, color, 2, cv::LINE_AA, 0);
	}
}

void Vision_API::drawOpencvContour(cv::Mat& src, std::vector<polygonPoints>& allContours, cv::Scalar color)
{
	for (auto& ply : allContours)
	{
		std::vector<cv::Point> points;
		for (auto& p : ply)
		{
			points.push_back(p);
		}
		cv::polylines(src, points, false, color, 1, cv::LINE_AA, 0);
	}
}
